home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / ghostscript / 8.64 / Resource / Init / pdf_rbld.ps < prev    next >
Encoding:
Text File  |  2009-04-17  |  14.7 KB  |  345 lines

  1. %    Copyright (C) 2002 Artifex Software, Inc.  All rights reserved.
  2. % This software is provided AS-IS with no warranty, either express or
  3. % implied.
  4. % This software is distributed under license and may not be copied,
  5. % modified or distributed except as expressly authorized under the terms
  6. % of the license contained in the file LICENSE in this distribution.
  7. % For more information about licensing, please refer to
  8. % http://www.ghostscript.com/licensing/. For information on
  9. % commercial licensing, go to http://www.artifex.com/licensing/ or
  10. % contact Artifex Software, Inc., 101 Lucas Valley Road #110,
  11. % San Rafael, CA  94903, U.S.A., +1(415)492-9861.
  12.  
  13. % $Id: pdf_rbld.ps 9175 2008-10-19 20:32:03Z alexcher $
  14. % pdf_rbld.ps - Rebuilding of broken PDF files (xref errors)
  15.  
  16. % This module contains routines that are used if we detect an error
  17. % while reading the xref tables.  These routines will scan the file and
  18. % build an xref table by finding the objects.  We also need to find the
  19. % appropriate trailer dictionary.  Note:  One procedure is also used
  20. % even if we do not need to rebuild a PDF file.
  21. %
  22. % This module cannot rebuild a PDF file which has had errors created inside
  23. % of objects or binary data streams.  It often succeeds with files that
  24. % have had its end of lines converted between unix and dos versions.
  25.  
  26. % if true --> we have an object with duplicate object and generation numbers.
  27. /dup_obj_gen_num false def
  28.  
  29. % Note:  This procedure is also used by non-rebuild code.
  30. % Store a line in the xref array (Actually Objects and Generations arrays)
  31. % <obj num> (strm num> <obj loc> <gen num> <rebuild>
  32. %                         setxrefentry <obj num> strm num> <obj loc> <gen num>
  33. /setxrefentry
  34. {
  35.   5 1 roll
  36.   dup 65535 or 65535 ne {
  37.     (   **** Warning:  Generation number out of 0..65535 range, assuming 0.\n)
  38.     pdfformaterror
  39.     pop 0
  40.   } if
  41.            % We store generation numbers as value + 1
  42.     % We reserve 0 to indicate an free xref entry
  43.   1 add            % increment generation number
  44.     % To save space, generations numbers are stored in a lstring unless we
  45.     % find a generation number greater than 255.  If so then transfer to 
  46.     % an larray.
  47.   dup 255 gt {
  48.     Generations ltype /stringtype eq {    % Convert Generations to an larray.
  49.       larray Generations llength lgrowto dup    % Create new larray
  50.       0 1 2 index llength 1 sub {    % Copy from old lstring to new larray
  51.     Generations 1 index lget lput dup
  52.       } for
  53.       pop
  54.       /Generations exch store        % Save new Generations larray
  55.     } if
  56.   } if
  57.     % Verify that the new values are for a new object.  If the current
  58.     % entry is null then we have a new entry.
  59.   Objects 4 index lget null eq {
  60.     ObjectStream 4 index 4 index cvx lput % Save ObjectStream object number
  61.     Objects 4 index 3 index cvx lput    % Save object location
  62.     Generations 4 index 2 index lput    % Save geenration number
  63.   } {
  64.     % Verify that the new entry has at least as high a generaton number
  65.     % We accept equal entry number because we have found PDF files in
  66.     % which there are multiple objects with the same object and entry
  67.     % numbers.  The normal xref logic only accepts the first such
  68.     % entry that it finds.  However the 'rebuild PDF' logic can find
  69.     % both such entries.  The correct one is usually the last one.
  70.     Generations 4 index lget 1 index le {
  71.       ObjectStream 4 index 4 index cvx lput % Save ObjectStream object number
  72.       Objects 4 index 3 index cvx lput    % Save object location
  73.       Generations 4 index 2 index lput    % Save geenration number
  74.     } if
  75.     % Set error flag if we have equal object and generation numbers
  76.     Generations 4 index lget 1 index eq { /dup_obj_gen_num true def } if
  77.   } 8 -1 roll { ifelse } { pop if } ifelse  % Run 'else' only when rebuilding.
  78. } bind def
  79.  
  80. % Print the contents of the xref array.  This actually consists of three
  81. % arrays (Objects, Generations, and ObjectStream).  All three are larrays.
  82. % larrays are a special Ghostscript object which can be arrays with more
  83. % than 64k elements.
  84. /print_xref                % - print_xref -
  85. { 0 1 Objects llength 1 sub        % stack: 0 1 <number of objects - 1>
  86.   { dup =only                % print object number
  87.     (  ) print
  88.     dup Generations exch lget 1 sub =only % print Generation number
  89.     (  ) print
  90.     dup ObjectStream exch lget ==only    % print ObjectStream object number
  91.     (  ) print
  92.     Objects exch lget ===        % print object location
  93.   } for
  94.   flush
  95. } bind def
  96.  
  97. % Get token from string and check its type
  98. %   <string> <type> typed_token <false>        % no token or not match
  99. %   <string> <type> typed_token <obj> <last> <true>    % matching token type
  100. % Where last is the string remainder
  101. /typed_token
  102. { exch
  103.   token_nofail            % get token
  104.   {
  105.     dup type            % stack:  type last token type
  106.     4 -1 roll eq {        % stack:  last token bool
  107.       exch true            % desired object found - set exit status
  108.     } {
  109.       pop pop false        % not type - clear stack, set exit status
  110.     } ifelse
  111.   } {
  112.     pop false            % no token - pop type, set exit status
  113.   } ifelse            % check if we got token
  114. } bind def
  115.  
  116. % Allocate space for post_eof_count to be bound into procedures below.
  117. /post_eof_count 0 def
  118.  
  119. % We want the location of the trailer dictionary at the start of file.
  120. % First we will find the xref.  Then we will skip over the xref entries
  121. % to the trailer.
  122. /search_start_trailer        % - search_start_trailer <trailer loc>
  123. { % Read the first 300 bytes and check for xref
  124.   PDFfile 0 setfileposition
  125.   PDFfile bytesavailable post_eof_count sub    % location of end of data
  126.   300 .min            % block size to read
  127.   dup string 0 1 4 -1 roll 1 sub
  128.   { 2 copy PDFfile read pop put pop } for
  129.   (xref) search {
  130.     % found 'xref'
  131.     exch pop exch pop length 4 add PDFfile exch setfileposition
  132.     PDFfile token pop        % get starting entry - or 'trailer'
  133.     (trailer) ne {        % if we do not already have 'trailer'
  134.       PDFfile token pop        % get number of entries
  135.       PDFfile token pop pop    % this moves us into the middle of the first entry
  136.       25 string exch        % define working string for readline
  137.       { PDFfile 1 index readline pop pop
  138.       } repeat            % skip entries
  139.       pop            % pop working string
  140.       PDFfile token pop pop    % get 'trailer'
  141.       PDFfile fileposition    % get file position
  142.     } if
  143.   } {
  144.     pop search_end_trailer    % no xref, should not happen, search end of file
  145.   } ifelse
  146. } bind def
  147.  
  148. % We want the location of the trailer dictionary at the end of file.
  149. % We will read the last block of data and search for the final occurance
  150. % of the word 'trailer'
  151. /search_end_trailer        % - search_end_trailer <trailer loc>
  152. { % Position to read block of data from the end of the file.  Note:  We ignore
  153.   % anything past the last %%EOF since this is not PDF data.
  154.   PDFfile 0 setfileposition
  155.   PDFfile bytesavailable post_eof_count sub    % location of end of data
  156.   dup 65535 .min        % block size to read
  157.                 % stack: <file end pos> <block size>
  158.   % move file position to the start of the block
  159.   2 copy sub PDFfile exch setfileposition
  160.   % read block of data
  161.   dup string 0 1 4 -1 roll 1 sub { 2 copy PDFfile read pop put pop } for
  162.   % search for last occurance of 'trailer'
  163.   (trailer) { search not { exit } if pop } loop
  164.   % determine where the trailer is in the file
  165.   %   trailer loc = end loc - remaing string length
  166.   length sub 
  167. } bind def
  168.  
  169. % We want to find the trailer dictionary.  There is a trailer dictionary
  170. % for each xref object list.  We only want the trailer dictionary associated
  171. % with the first xref object list.  In theory this can be anywhere in the
  172. % file.  However since we are trying to repair a broken file, we cannot simply
  173. % follow the xref links.  So we are falling back to a simple strategy.  We
  174. % find the specified location of the first xref list.  If its location is in
  175. % the first half of the file then we search for the first trailer dictionary
  176. % at the start of the file.  Otherwise we search for the last trailer at the
  177. % end of the file.
  178. /search_trailer            % - search_trailer -
  179. { % Find the 'startxref' and associated position at the end of the file.
  180.   % Position to read block of data from the end of the file.  Note:  We
  181.   % actually end at the end of the last %%EOF since this is the end of the
  182.   % useful PDF data.  (Some files contain trailing garbage.)
  183.   PDFfile 0 setfileposition
  184.   PDFfile bytesavailable    % size of file
  185.   post_eof_count sub dup    % location of end of last %%EOF
  186.   dup 4096 .min            % block size to read
  187.   % stack: <useful file size> <useful file size file> <block size>
  188.   % move file position to the start of the block
  189.   2 copy sub PDFfile exch setfileposition
  190.   % read block of data
  191.   dup string 0 1 4 -1 roll 1 sub { 2 copy PDFfile read pop put pop } for
  192.   % search for last occurance of 'startxref'
  193.   false                % Assume that startxref not present
  194.   exch (startxref) { 
  195.      search not { exit } if    % Exit loop when no more startxref's
  196.      pop 3 -1 roll pop true 3 1 roll    % Idicate that we have found starxref
  197.   } loop
  198.   exch                % Exch last string and 'found' flag
  199.   { 
  200.      % determine where the startxref is in the file
  201.      %   'startxref' loc = end loc - remaing string length - 9 bytes
  202.      length sub 9 sub
  203.      % move the file to this position and read startxref and position
  204.      PDFfile exch setfileposition PDFfile token
  205.      pop pop PDFfile token pop
  206.   } {
  207.      % startxref not found.  We will search the end of the file for trailer.
  208.      pop pop PDFfilelen
  209.   } ifelse
  210.   % compare xref position to 1/2 the length of the file and search for trailer
  211.   exch 2 div lt { search_start_trailer } { search_end_trailer } ifelse
  212.   % get the trailer
  213.   PDFfile exch setfileposition        % set to the specified trailer location
  214.   /dictlevelcount 0 def
  215.   PDFfile traileropdict .pdfrun        % read trailer info
  216.   /Trailer exch def
  217. } bind def
  218.  
  219. % This routine will determine if there is stuff after the %%EOF.  There is
  220. % supposed to be only a line termination.  However many real life files
  221. % contain some garbage.  This routine checks how much.  We then ignore this
  222. % stuff when we are scanning for objects.
  223. /determine_post_eof_count        % - determine_post_eof_count <count>
  224. { % Position to read block of data from the end of the file. 
  225.   PDFfilelen            % size of file
  226.   dup 4096 .min                    % file_size block_size
  227.   dup 3 1 roll sub              % block_size file_size-block_size 
  228.   PDFfile exch setfileposition  % block_size
  229.   string PDFfile exch readstring pop % ()
  230.  
  231.   % search for last occurance of 'startxref', '%%EOF' is often damaged
  232.   (startxref) {
  233.     search not { exit } if pop
  234.   } loop
  235.   % how much is left = remaining string length
  236.  
  237.   % Now search for %%EO or try to read a number after 'startxref'.
  238.   (%%EO) search {
  239.     pop pop 
  240.   } {
  241.     % Look for a number after startxref
  242.     { dup token { pop exch pop } if
  243.     } stopped {
  244.       pop
  245.     } if
  246.   } ifelse
  247.   length
  248. } bind def
  249.  
  250. % This routine will scan a file searaching for object locations to build
  251. % an alternate version of the data in the xref tables.
  252. % Its purpose is to provide a basis for an xref fixing facility.
  253. /search_objects                % - search_objects -
  254. { % Initialize the Objects, Generations, etc. larrays
  255.   initPDFobjects
  256.   % reset duplicate object and generation numbers error flag
  257.   /dup_obj_gen_num false def
  258.   % Determine how many bytes are in the file after the final %%EOF
  259.   /post_eof_count determine_post_eof_count def
  260.   % Start at the beginning of the file
  261.   PDFfile 0 setfileposition
  262.   % Create a working string (and also store its length on stack).  We are
  263.   % using a maximum size string size the logic below wants a recovered object
  264.   % to fit into our working string.
  265.   65535 dup string
  266.   { % Now loop through the entire file lloking for objects
  267.     PDFfile fileposition        % save current file position
  268.     % When we get near the end of the file, we use a smaller interval of
  269.     % our working string to prevent reading past the end.  (See comments on
  270.     % EOF testing below.)
  271.     PDFfile bytesavailable post_eof_count sub 10 sub dup 4 index lt {
  272.       2 index 0 3 -1 roll getinterval    % near EOF, use interval of string
  273.     } { pop 1 index            % not near end, use full working string
  274.     }ifelse
  275.     % Read a line from file.  If the line does not fit into our working string,
  276.     % or any other error, then we will discard it.
  277.     PDFfile exch { readline } .internalstopped
  278.     { pop pop false } if        % indicate no string if we stopped
  279.     { % stack: <length> <working_str> <loc> <string>
  280.       % Now that we have line, get obj num, ref num, and 'obj'.  Verify that each
  281.       % of these is correct type.
  282.       /integertype typed_token {    % get obj number
  283.         /integertype typed_token {    % get ref number
  284.           /nametype typed_token {    % get 'obj' text
  285.         pop                % pop remaining string
  286.         /obj eq {            % verify name is 'obj'
  287.           % make sure we have room in the arrays.  We work in increments
  288.           % of 20 each time we increase the size.
  289.           1 index 20 add 20 idiv 20 mul
  290.           growPDFobjects
  291.           % save xref parameters into ObjectStream, Objects and Generations
  292.           1 index 0            % rearrange parms for setxrefentry
  293.           4 index PDFoffset sub 3 index
  294.           //true setxrefentry    % save parameters
  295.           pop pop pop pop        % clear parameters
  296.         } if            % check if name is 'obj'
  297.           } if                % check if we got 'obj" string
  298.           pop                % remove ref number
  299.         } if                % check if we got ref number
  300.         pop                % remove obj number
  301.       } if                % check if we got object number
  302.     } if                % check if got a string from readline
  303.     pop                    % remove location
  304.     % Check if we are approaching the end of the file.  We do not want to
  305.     % read past the end of the file since that closes it.  We actually stop
  306.     % 10-20 bytes early since there cannot be an object that close to the end.
  307.     % (There is a Trailer dictionary, etc. at the end of the file.)
  308.     PDFfile bytesavailable post_eof_count sub 20 lt { exit } if
  309.   } loop                % loop through the entire file
  310.   pop pop                % remove working string and its length
  311.   % Output warning if we have two objects with the same object and generation
  312.   % numbers.
  313.   dup_obj_gen_num {
  314.     (   **** Warning:  There are objects with matching object and generation\n)
  315.     pdfformaterror
  316.     (   **** numbers.  The accuracy of the resulting image is unknown.\n)
  317.     pdfformaterror
  318.   } if
  319. } bind def
  320.  
  321. % Print warning message because we found a problem while reading the xref
  322. % tables
  323. /print_xref_warning
  324. { (   **** Warning:  An error occurred while reading an XREF table.\n)
  325.   pdfformaterror
  326.   (   **** The file has been damaged.  This may have been caused\n)
  327.   pdfformaterror
  328.   (   **** by a problem while converting or transfering the file.\n)
  329.   pdfformaterror
  330.   (   **** Ghostscript will attempt to recover the data.\n)
  331.   pdfformaterror
  332. } bind def
  333.  
  334. % Attempt to recover the XRef data.  This is called if we have a failure
  335. % while reading the normal XRef tables.  This routine usually works
  336. % only for pre PDF1.5 versions of PDF files.
  337. /recover_xref_data        % - recover_xref_data -
  338. { print_xref_warning        % Print warning message
  339.   count pdfemptycount sub { pop } repeat % remove anything left by readxref
  340.   search_objects        % Search for objects
  341. } bind def
  342.